package io.shuttle.mbe.api

import io.shuttle.mbe.api.Events.Event
import io.shuttle.mbe.api.annotation.ChromeMinVersion
import io.shuttle.mbe.api.annotation.PromiseStyleMinVersion
import io.shuttle.mbe.api.types.Value1Function
import io.shuttle.mbe.core.Promise

////////////////////
// Web Navigation
////////////////////
/**
 * Use the `Browser.webNavigation` API to receive notifications about the status of navigation requests in-flight.
 *
 * Permissions: "webNavigation"
 */
/// EventOrder
/// For a navigation that is successfully completed, events are fired in the following order:
///  onBeforeNavigate -> onCommitted -> [onDOMContentLoaded] -> onCompleted
interface WebNavigation {

    // Retrieves information about all frames of a given tab.
    @PromiseStyleMinVersion(93)
    fun getAllFrames(
        details: GetAllFramesDetails,
        callback: Value1Function<List<GetAllFrameResult>?>? = null
    ): Promise<List<GetAllFrameResult>?>

    // Retrieves information about the given frame. A frame refers to an <iframe> or a <frame> of a web page and is identified by a tab ID and a frame ID.
    @PromiseStyleMinVersion(93)
    fun getFrame(
        details: GetFrameDetails,
        callback: Value1Function<GetFrameResult?>? = null
    ): Promise<GetFrameResult?>

    // Fired when a navigation is about to occur.
    val onBeforeNavigate: WebNavigationEvent<WebNavigationParentedCallbackDetails>

    // Fired when a navigation is committed. The document (and the resources it refers to, such as images and subframes) might still be downloading, but at least part of the document has been received from the server and the browser has decided to switch to the new document.
    val onCommitted: WebNavigationEvent<WebNavigationTransitionCallbackDetails>

    // Fired when a document, including the resources it refers to, is completely loaded and initialized.
    val onCompleted: WebNavigationEvent<WebNavigationFramedCallbackDetails>

    // Fired when a new window, or a new tab in an existing window, is created to host a navigation.
    val onCreatedNavigationTarget: WebNavigationEvent<WebNavigationSourceCallbackDetails>

    // Fired when the page's DOM is fully constructed, but the referenced resources may not finish loading.
    val onDOMContentLoaded: WebNavigationEvent<WebNavigationFramedCallbackDetails>

    // Fired when an error occurs and the navigation is aborted. This can happen if either a network error occurred, or the user aborted the navigation.
    val onErrorOccurred: WebNavigationEvent<WebNavigationFramedErrorCallbackDetails>

    // Fired when the frame's history was updated to a new URL. All future events for that frame will use the updated URL.
    val onHistoryStateUpdated: WebNavigationEvent<WebNavigationTransitionCallbackDetails>

    // Fired when the reference fragment of a frame was updated. All future events for that frame will use the updated URL.
    val onReferenceFragmentUpdated: WebNavigationEvent<WebNavigationTransitionCallbackDetails>

    // Fired when the contents of the tab is replaced by a different (usually previously pre-rendered) tab.
    val onTabReplaced: WebNavigationEvent<WebNavigationReplacementCallbackDetails>

    @ChromeMinVersion(44)
    enum class TransitionQualifier {
        client_redirect,
        server_redirect,
        forward_back,
        from_address_bar,
    }

    // Cause of the navigation. The same transition types as defined in the history API are used. These are the same transition types as defined in the history API except with "start_page" in place of "auto_toplevel" (for backwards compatibility).
    @ChromeMinVersion(44)
    enum class TransitionType {
        link,
        typed,
        auto_bookmark,
        auto_subframe,
        manual_subframe,
        generated,
        start_page,
        form_submit,
        reload,
        keyword,
        keyword_generated,
    }

    data class GetAllFramesDetails(
        var tabId: Number,
    )

    data class GetAllFrameResult(
        // A UUID of the document loaded.
        @ChromeMinVersion(106)
        var documentId: String,
        // The lifecycle the document is in.
        @ChromeMinVersion(106)
        var documentLifecycle: ExtensionTypes.DocumentLifecycle,
        // True if the last navigation in this frame was interrupted by an error, i.e. the onErrorOccurred event fired.
        var errorOccurred: Boolean,
        // The ID of the frame. 0 indicates that this is the main frame; a positive value indicates the ID of a subframe.
        var frameId: Number,
        // The type of frame the navigation occurred in.
        @ChromeMinVersion(106)
        var frameType: ExtensionTypes.FrameType,
        // A UUID of the parent document owning this frame. This is not set if there is no parent.
        @ChromeMinVersion(106)
        var parentDocumentId: String?,
        // The ID of the parent frame, or -1 if this is the main frame.
        var parentFrameId: Number,
        // The ID of the process that runs the renderer for this frame.
        var processId: Number,
        // The URL currently associated with this frame.
        var url: String,
    )

    data class GetFrameDetails(
        // The UUID of the document. If the frameId and/or tabId are provided they will be validated to match the document found by provided document ID.
        @ChromeMinVersion(106)
        var documentId: String?,
        // The ID of the frame in the given tab.
        var frameId: Number?,
        // The ID of the process that runs the renderer for this tab.
        @Deprecated(
            "Deprecated since Chrome 49",
            replaceWith = ReplaceWith("Frames are now uniquely identified by their tab ID and frame ID; the process ID is no longer needed and therefore ignored.")
        )
        var processId: Number?,
        // The ID of the tab in which the frame is.
        var tabId: Number?,
    )

    data class GetFrameResult(
        // A UUID of the document loaded.
        @ChromeMinVersion(106)
        var documentId: String,
        // The lifecycle the document is in.
        @ChromeMinVersion(106)
        var documentLifecycle: ExtensionTypes.DocumentLifecycle,
        // True if the last navigation in this frame was interrupted by an error, i.e. the onErrorOccurred event fired.
        var errorOccurred: Boolean,
        // The type of frame the navigation occurred in.
        @ChromeMinVersion(106)
        var frameType: ExtensionTypes.FrameType,
        // A UUID of the parent document owning this frame. This is not set if there is no parent.
        @ChromeMinVersion(106)
        var parentDocumentId: String?,
        // The ID of the parent frame, or -1 if this is the main frame.
        var parentFrameId: Number,
        // The URL currently associated with this frame, if the frame identified by the frameId existed at one point in the given tab. The fact that an URL is associated with a given frameId does not imply that the corresponding frame still exists.
        var url: String,
    )

    data class NavigationEventDetails(
        // The lifecycle the document is in.
        @ChromeMinVersion(106)
        var documentLifecycle: ExtensionTypes.DocumentLifecycle,
        // 0 indicates the navigation happens in the tab content window; a positive value indicates navigation in a subframe. Frame IDs are unique for a given tab and process.
        var frameId: Number,
        // The type of frame the navigation occurred in.
        @ChromeMinVersion(106)
        var frameType: ExtensionTypes.FrameType,
        // A UUID of the parent document owning this frame. This is not set if there is no parent.
        @ChromeMinVersion(106)
        var parentDocumentId: String?,
        // The ID of the parent frame, or -1 if this is the main frame.
        var parentFrameId: Number,
        // The value of -1.
        @Deprecated(
            "Deprecated since Chrome 49",
            replaceWith = ReplaceWith("Frames are now uniquely identified by their tab ID and frame ID; the process ID is no longer needed and therefore ignored.")
        )
        var processId: Number?,
        // The ID of the tab in which the navigation is about to occur.
        var tabId: Number?,
        // The time when the browser was about to start the navigation, in milliseconds since the epoch.
        var timeStamp: Number,
        var url: String
    )

    data class EventFilters(
        // Conditions that the URL being navigated to must satisfy. The 'schemes' and 'ports' fields of UrlFilter are ignored for this event.
        var url: List<Events.UrlFilter>
    )

    data class CommitedEventDetails(
        // A UUID of the document loaded.
        @ChromeMinVersion(106)
        var documentId: String,
        // The lifecycle the document is in.
        @ChromeMinVersion(106)
        var documentLifecycle: ExtensionTypes.DocumentLifecycle,
        // The ID of the frame. 0 indicates that this is the main frame; a positive value indicates the ID of a subframe.
        var frameId: Number,
        // The type of frame the navigation occurred in.
        @ChromeMinVersion(106)
        var frameType: ExtensionTypes.FrameType,
        // A UUID of the parent document owning this frame. This is not set if there is no parent.
        @ChromeMinVersion(106)
        var parentDocumentId: String?,
        // The ID of the parent frame, or -1 if this is the main frame.
        @ChromeMinVersion(74)
        var parentFrameId: Number,
        // The ID of the process that runs the renderer for this frame.
        var processId: Number,
        // The ID of the tab in which the navigation occurs.
        var tabId: Number,
        // The time when the navigation was committed, in milliseconds since the epoch.
        var timeStamp: Number,
        // A list of transition qualifiers.
        var transitionQualifiers: List<TransitionQualifier>,
        // Cause of the navigation.
        var transitionType: TransitionType,
        var url: String
    )

    data class CompletedEventDetails(
        // A UUID of the document loaded.
        @ChromeMinVersion(106)
        var documentId: String,
        // The lifecycle the document is in.
        @ChromeMinVersion(106)
        var documentLifecycle: ExtensionTypes.DocumentLifecycle,
        // The ID of the frame. 0 indicates that this is the main frame; a positive value indicates the ID of a subframe.
        var frameId: Number,
        // The type of frame the navigation occurred in.
        @ChromeMinVersion(106)
        var frameType: ExtensionTypes.FrameType,
        // A UUID of the parent document owning this frame. This is not set if there is no parent.
        @ChromeMinVersion(106)
        var parentDocumentId: String?,
        // The ID of the parent frame, or -1 if this is the main frame.
        @ChromeMinVersion(74)
        var parentFrameId: Number,
        // The ID of the process that runs the renderer for this frame.
        var processId: Number,
        // The ID of the tab in which the navigation occurs.
        var tabId: Number,
        // The time when the navigation was committed, in milliseconds since the epoch.
        var timeStamp: Number,
        var url: String
    )

    data class CreatedNavigationTargetEventDetails(
        // The ID of the frame with sourceTabId in which the navigation is triggered. 0 indicates the main frame.
        var sourceFrameId: Number,
        // The ID of the process that runs the renderer for the source frame.
        var sourceProcessId: Number,
        // The ID of the tab in which the navigation is triggered.
        var sourceTabId: Number,
        // The ID of the tab in which the url is opened
        var tabId: Number,
        // The time when the browser was about to create a new view, in milliseconds since the epoch.
        var timeStamp: Number,
        // The URL to be opened in the new window.
        var url: String
    )

    data class ErrorOccurredEventDetails(
        // A UUID of the document loaded.
        @ChromeMinVersion(106)
        var documentId: String,
        // The lifecycle the document is in.
        @ChromeMinVersion(106)
        var documentLifecycle: ExtensionTypes.DocumentLifecycle,
        // The error description.
        var error: String,
        // The ID of the frame. 0 indicates that this is the main frame; a positive value indicates the ID of a subframe.
        var frameId: Number,
        // The type of frame the navigation occurred in.
        @ChromeMinVersion(106)
        var frameType: ExtensionTypes.FrameType,
        // A UUID of the parent document owning this frame. This is not set if there is no parent.
        @ChromeMinVersion(106)
        var parentDocumentId: String?,
        // The ID of the parent frame, or -1 if this is the main frame.
        @ChromeMinVersion(74)
        var parentFrameId: Number,
        // The value of -1.
        @Deprecated(
            "Deprecated since Chrome 50",
            replaceWith = ReplaceWith("The processId is no longer set for this event.")
        )
        var processId: Number,
        // The ID of the tab in which the navigation occurs.
        var tabId: Number,
        // The time when the navigation was committed, in milliseconds since the epoch.
        var timeStamp: Number,
        var url: String
    )

    data class TabNavigationEventDetails(
        // The ID of the tab that was replaced.
        var replacedTabId: Number,
        // The ID of the tab that replaced the old tab.
        var tabId: Number,
        // The time when the replacement happened, in milliseconds since the epoch.
        var timeStamp: Number,
    )

    interface WebNavigationCallbackDetails {
        var tabId: Number
        var timeStamp: Number
    }

    interface WebNavigationUrlCallbackDetails : WebNavigationCallbackDetails {
        var url: String
    }

    interface WebNavigationReplacementCallbackDetails : WebNavigationUrlCallbackDetails {
        var replacedTabId: Number
    }

    interface WebNavigationFramedCallbackDetails : WebNavigationUrlCallbackDetails {
        var frameId: Number
        var frameType: ExtensionTypes.FrameType
        var documentId: String?
        var documentLifecycle: ExtensionTypes.DocumentLifecycle
        var parentDocumentId: String?

        @ChromeMinVersion(22)
        var processId: Number
    }

    interface WebNavigationFramedErrorCallbackDetails : WebNavigationFramedCallbackDetails {
        var error: String
    }

    interface WebNavigationSourceCallbackDetails : WebNavigationUrlCallbackDetails {
        var sourceFrameId: Number
        var sourceProcessId: Number
        var sourceTabId: Number
    }

    interface WebNavigationParentedCallbackDetails : WebNavigationFramedCallbackDetails {
        var parentFrameId: Number
    }

    interface WebNavigationTransitionCallbackDetails : WebNavigationFramedCallbackDetails {
        var transitionQualifiers: List<TransitionQualifier>
        var transitionType: TransitionType
    }

    interface WebNavigationEventFilter {
        var url: List<Events.UrlFilter>
    }


    interface WebNavigationEvent<T : WebNavigationCallbackDetails> : Event<Value1Function<T>> {
        fun addListener(callback: Value1Function<T>, filters: WebNavigation.EventFilters)
    }

}